Atomic Variables এমন ডেটা টাইপ যা মাল্টি-থ্রেডেড পরিবেশে নিরাপদ এবং কার্যকরভাবে কাজ করতে পারে। Java এর AtomicReference, AtomicInteger, AtomicLong এবং অন্যান্য atomic classes ব্যবহার করে আমরা একাধিক থ্রেডের মধ্যে stack, queue বা অন্যান্য ডেটা স্ট্রাকচার তৈরি করতে পারি যা লক-মুক্ত এবং কার্যকরী।
নিচে Atomic Variables ব্যবহার করে Stack এবং Queue তৈরির উদাহরণ দেওয়া হল।
Atomic Stack তৈরি করা
Stack হল একটি LIFO (Last In, First Out) ডেটা স্ট্রাকচার। আমাদের AtomicReference ব্যবহার করে Thread-safe stack তৈরি করা সম্ভব। এখানে, আমরা push() এবং pop() অপারেশনগুলোর জন্য atomic operations ব্যবহার করব।
Atomic Stack এর উদাহরণ:
import java.util.concurrent.atomic.AtomicReference;
class AtomicStack<T> {
private AtomicReference<Node<T>> top = new AtomicReference<>(null);
// Node class to represent stack elements
static class Node<T> {
T value;
Node<T> next;
Node(T value) {
this.value = value;
}
}
// Push method
public void push(T value) {
Node<T> newNode = new Node<>(value);
Node<T> oldTop;
do {
oldTop = top.get();
newNode.next = oldTop;
} while (!top.compareAndSet(oldTop, newNode)); // CAS operation
}
// Pop method
public T pop() {
Node<T> oldTop;
Node<T> newTop;
do {
oldTop = top.get();
if (oldTop == null) {
return null; // Stack is empty
}
newTop = oldTop.next;
} while (!top.compareAndSet(oldTop, newTop)); // CAS operation
return oldTop.value;
}
// Peek method to view top element
public T peek() {
Node<T> oldTop = top.get();
return (oldTop == null) ? null : oldTop.value;
}
}
public class AtomicStackExample {
public static void main(String[] args) {
AtomicStack<Integer> stack = new AtomicStack<>();
// Push elements to stack
stack.push(1);
stack.push(2);
stack.push(3);
// Pop elements from stack
System.out.println(stack.pop()); // 3
System.out.println(stack.pop()); // 2
System.out.println(stack.pop()); // 1
System.out.println(stack.pop()); // null (Stack is empty)
}
}
ব্যাখ্যা:
- AtomicReference<Node> top: এটি স্ট্যাকের শীর্ষ (top) নোডটিকে ট্র্যাক করে।
- CAS Operation:
compareAndSet()ফাংশন ব্যবহার করে push() এবং pop() অপারেশন সুরক্ষিতভাবে সম্পন্ন হয়। এটি নিশ্চিত করে যে, স্ট্যাকের শীর্ষ মান আপডেট হবে যদি এবং শুধুমাত্র যদি, বর্তমান শীর্ষ মানটি পুরনো মানের সাথে মিলে যায়।
Atomic Queue তৈরি করা
Queue হল একটি FIFO (First In, First Out) ডেটা স্ট্রাকচার। একটি থ্রেড সেফ queue তৈরি করতে, আমরা AtomicReference ব্যবহার করতে পারি যাতে একাধিক থ্রেড নিরাপদভাবে ডেটা যোগ (enqueue) এবং সরাতে (dequeue) পারে।
Atomic Queue এর উদাহরণ:
import java.util.concurrent.atomic.AtomicReference;
class AtomicQueue<T> {
private AtomicReference<Node<T>> head = new AtomicReference<>(null);
private AtomicReference<Node<T>> tail = new AtomicReference<>(null);
// Node class for queue elements
static class Node<T> {
T value;
Node<T> next;
Node(T value) {
this.value = value;
}
}
// Enqueue method
public void enqueue(T value) {
Node<T> newNode = new Node<>(value);
Node<T> oldTail;
do {
oldTail = tail.get();
if (oldTail == null) {
head.set(newNode);
} else {
oldTail.next = newNode;
}
} while (!tail.compareAndSet(oldTail, newNode)); // CAS operation
}
// Dequeue method
public T dequeue() {
Node<T> oldHead;
Node<T> newHead;
do {
oldHead = head.get();
if (oldHead == null) {
return null; // Queue is empty
}
newHead = oldHead.next;
} while (!head.compareAndSet(oldHead, newHead)); // CAS operation
return oldHead.value;
}
// Peek method to view front element
public T peek() {
Node<T> oldHead = head.get();
return (oldHead == null) ? null : oldHead.value;
}
}
public class AtomicQueueExample {
public static void main(String[] args) {
AtomicQueue<Integer> queue = new AtomicQueue<>();
// Enqueue elements to queue
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
// Dequeue elements from queue
System.out.println(queue.dequeue()); // 1
System.out.println(queue.dequeue()); // 2
System.out.println(queue.dequeue()); // 3
System.out.println(queue.dequeue()); // null (Queue is empty)
}
}
ব্যাখ্যা:
- AtomicReference<Node> head এবং tail: Queue এর প্রথম (head) এবং শেষ (tail) নোডগুলি ট্র্যাক করতে ব্যবহৃত হয়।
- CAS Operation: enqueue() এবং dequeue() অপারেশনগুলি atomic CAS অপারেশন ব্যবহার করে নিশ্চিত করে যে, একাধিক থ্রেড সিঙ্ক্রোনাইজডভাবে ডেটা যোগ এবং সরাতে পারে।
Atomic Stack এবং Queue এর সুবিধা
- Thread-Safety: একাধিক থ্রেড একযোগে কাজ করলেও ডেটার সঠিকতা নিশ্চিত থাকে।
- Lock-Free:
compareAndSet()অপারেশন ব্যবহার করে লক-মুক্ত স্ট্রাকচার তৈরি করা হয়, যা পারফরম্যান্স বাড়ায়। - Concurrency Optimization: বিভিন্ন থ্রেডের মধ্যে সমান্তরাল কাজকে সমর্থন করে, যা মাল্টি-থ্রেডেড অ্যাপ্লিকেশনগুলির জন্য উপকারী।
- Scalability: থ্রেড নিরাপদ অপারেশনসমূহ ব্যবহার করে কোডের স্কেলেবিলিটি উন্নত হয়।
Atomic Stack এবং Queue এর সীমাবদ্ধতা
- Complexity: atomic operations যেমন
compareAndSet()কিছুটা জটিল এবং বিভিন্ন ক্ষেত্রে spinning এর কারণে CPU overhead বাড়াতে পারে। - Limited to Single Element: এই ডেটা স্ট্রাকচারগুলি একযোগভাবে শুধুমাত্র একটি এন্ট্রি ম্যানেজ করতে পারে। অনেক সময় জটিল ডেটা স্ট্রাকচার ব্যবহারে এটি কার্যকর নয়।
- Atomicity Issues: যদি একটি প্রক্রিয়া অনেক বেশি সময় নিয়ে থাকে, তবে এটি অন্যান্য থ্রেডের জন্য সমস্যার সৃষ্টি করতে পারে।
উপসংহার
Atomic Variables যেমন AtomicReference এবং CAS (Compare-And-Swap) মেকানিজম ব্যবহার করে Thread-safe Stack এবং Queue তৈরি করা যায়, যা মাল্টি-থ্রেডেড প্রোগ্রামিংয়ে পারফরম্যান্স এবং ডেটার সঠিকতা নিশ্চিত করে। এগুলো লক-মুক্ত অপারেশন এবং উচ্চ পারফরম্যান্সের জন্য অত্যন্ত কার্যকরী, তবে সঠিক ব্যবহারের জন্য এটি কিছু পরিমাণে জটিল হতে পারে।
Read more